#include "Common.h"
#include "Entity.h"
#include "MeshManager.h"
#include "Mesh.h"
#include "Skeleton.h"
#include "MaterialManager.h"
#include "Material.h"
#include "RenderView.h"
#include "SceneNode.h"

//
// SubEntity
//
SubEntity::SubEntity(Entity* parent, SubMesh* subMesh)
	: mParent(parent)
	, mSubMesh(subMesh)
{
	Material* material = MaterialManager::Instance().GetByName(mSubMesh->getMaterialName());
	if (material != NULL)
		mMaterialInstance = material->GetDefaultInstance();
	else
		mMaterialInstance = MaterialManager::Instance().getDefaultMaterial()->GetDefaultInstance();
}

SubEntity::~SubEntity()
{
}

VertexFactory* SubEntity::getVertexFactory()
{
	return mSubMesh->getVertexFactory();
}

MaterialInstance* SubEntity::getMaterial()
{
	return mMaterialInstance;
}

const IndexData* SubEntity::getIndexData() const
{
	return mSubMesh->getIndexData();
}

size_t SubEntity::GetGpuSkinMatrix(Matrix3x4* xform) const
{
	// check this entity has skeleton.
	const SkeletonInstance* skeletonInstance = mParent->getSkeletonInstance();
	if (skeletonInstance == NULL)
		return 0;

	// fetch the bone index map.
	const IndexMap& indexMap = mSubMesh->getBoneIndexMap().empty() ? mSubMesh->getParent()->getBoneIndexMap() : mSubMesh->getBoneIndexMap();
	if (indexMap.empty())
		return 0;

	// fetch the skeleton array.
	const BoneMatrices& boneMatrices = skeletonInstance->getBoneMatrices();
	for (IndexMap::const_iterator it(indexMap.begin()), end(indexMap.end()); it != end; it++)
		*xform++ = boneMatrices.Get(*it);

	return indexMap.size();
}

//
// Entity
//
Entity::Entity(const std::string& name, Scene* owner)
	: MovableObject(name, owner)
	, mSkeletonInstance(NULL)
{
}

Entity::~Entity()
{
	for (SubEntityList::iterator it(mSubEntities.begin()), end(mSubEntities.end()); it != end; it++)
	{
		SubEntity* subEntity = *it;
		ALIGN_DELETE(SubEntity, subEntity);
	}
	mSubEntities.clear();

	SAFE_DELETE(mSkeletonInstance);
}

	
bool Entity::Init(const NameValeParamMap* params)
{
	if (!MovableObject::Init(params))
		return false;

	if (params == NULL)
		return false;

	NameValeParamMap::const_iterator it = params->find("mesh");
	if (it == params->end())
		return false;

	mMeshName = it->second;
	mMesh = MeshManager::Instance().Load(mMeshName);
	if (mMesh == NULL)
		return false;

	// skeleton
	if (mMesh->getSkeleton() != NULL)
		mSkeletonInstance = new SkeletonInstance(this, mMesh->getSkeleton());

	const Mesh::SubMeshList& subMeshes = mMesh->getSubMeshes();
	for (Mesh::SubMeshList::const_iterator it(subMeshes.begin()), end(subMeshes.end()); it != end; it++)
	{
		SubEntity* subEntity = ALIGN_NEW(SubEntity, (this, *it));
		mSubEntities.push_back(subEntity);
	}

	mAABB = mMesh->getBounds();

	return true;
}

void Entity::AddToView(RenderView* view)
{
	for (SubEntityList::iterator it(mSubEntities.begin()), end(mSubEntities.end()); it != end; it++)
		view->AddRenderable(*it);
}

void Entity::UpdateTransform()
{
	MovableObject::UpdateTransform();
	
	for (SubEntityList::iterator it(mSubEntities.begin()), end(mSubEntities.end()); it != end; it++)
		(*it)->setWorldMatrix(mParent->getCachedMatrix());
}

void Entity::AddToScene()
{
	MovableObject::AddToScene();

	for (SubEntityList::iterator it(mSubEntities.begin()), end(mSubEntities.end()); it != end; it++)
		(*it)->AddToScene(mOwner);
}

void Entity::RemoveFromScene()
{
	MovableObject::RemoveFromScene();

	for (SubEntityList::iterator it(mSubEntities.begin()), end(mSubEntities.end()); it != end; it++)
		(*it)->RemoveFromScene(mOwner);
}

void Entity::Update(float elapsedTime)
{
	if (mSkeletonInstance != NULL)
		mSkeletonInstance->Update(elapsedTime);
}